# StalactiteRepositoriesAutoConfiguration.java

    package org.codefilarete.stalactite.spring.autoconfigure;

    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;

    import org.codefilarete.stalactite.engine.ConnectionSettings;
    import org.codefilarete.stalactite.engine.DatabaseVendorSettings;
    import org.codefilarete.stalactite.engine.PersistenceContext;
    import org.codefilarete.stalactite.spring.autoconfigure.StalactiteRepositoriesAutoConfiguration.StalactiteRepositoriesImportSelector;
    import org.codefilarete.stalactite.spring.repository.StalactiteRepository;
    import org.codefilarete.stalactite.spring.repository.StalactiteRepositoryFactoryBean;
    import org.codefilarete.stalactite.spring.repository.config.EnableStalactiteRepositories;
    import org.codefilarete.stalactite.spring.repository.config.SpringDataPersistenceContextConfigurationBuilder;
    import org.codefilarete.stalactite.spring.transaction.StalactitePlatformTransactionManager;
    import org.codefilarete.stalactite.sql.Dialect;
    import org.codefilarete.stalactite.sql.DialectResolver;
    import org.codefilarete.stalactite.sql.ServiceLoaderDialectResolver;
    import org.springframework.beans.factory.ObjectProvider;
    import org.springframework.boot.autoconfigure.AutoConfiguration;
    import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
    import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Import;
    import org.springframework.context.annotation.ImportSelector;
    import org.springframework.core.type.AnnotationMetadata;

    import static org.codefilarete.stalactite.engine.PersistenceContextConfigurationBuilder.PersistenceContextConfiguration;
    import static org.codefilarete.tool.Nullable.nullable;

    /**
     * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Stalactite Repositories.
     * <p>
     * Activates when there is a bean of type {@link javax.sql.DataSource} configured in the
     * context, the Spring Data Stalactite {@link StalactiteRepository} type is on the classpath, and there
     * is no other, existing {@link StalactiteRepository} configured.
     * <p>
     * Once in effect, the auto-configuration is the equivalent of enabling Stalactite repositories
     * using the {@link EnableStalactiteRepositories @EnableStalactiteRepositories} annotation.
     * <p>
     *
     * @author Guillaume Mary
     * @see org.codefilarete.stalactite.spring.repository.config.EnableStalactiteRepositories
     */
    @AutoConfiguration(after = { StalactiteAutoConfiguration.class, TaskExecutionAutoConfiguration.class })
    @ConditionalOnBean(value = { DataSource.class })
    @ConditionalOnClass(StalactiteRepository.class)
    @ConditionalOnMissingBean({ StalactiteRepositoryFactoryBean.class })
    @ConditionalOnProperty(prefix = "spring.data.stalactite.repositories", name = "enabled", havingValue = "true", matchIfMissing = true)
    @Import(StalactiteRepositoriesImportSelector.class)
    public class StalactiteRepositoriesAutoConfiguration {

    	@Bean
    	@ConditionalOnMissingBean
    	public PersistenceContext persistenceContext(PersistenceContextConfiguration persistenceContextConfiguration, Dialect dialect) {
    		return new PersistenceContext(persistenceContextConfiguration.getConnectionConfiguration(), dialect);
    	}

    	/**
    	 * Creates a {@link SpringDataPersistenceContextConfigurationBuilder} and returns its {@link PersistenceContextConfiguration}, hence making
    	 * available a {@link StalactitePlatformTransactionManager} through <code>persistenceContextConfiguration.getConnectionConfiguration().getConnectionProvider()</code>
    	 *
    	 * @param databaseVendorSettings
    	 * @param connectionSettings
    	 * @return
    	 */
    	@Bean
    	@ConditionalOnMissingBean
    	public PersistenceContextConfiguration persistenceContextConfigurationBuilder(DatabaseVendorSettings databaseVendorSettings, ConnectionSettings connectionSettings, DataSource dataSource) {
    		SpringDataPersistenceContextConfigurationBuilder persistenceContextConfigurationBuilder = new SpringDataPersistenceContextConfigurationBuilder(databaseVendorSettings, connectionSettings, dataSource);
    		return persistenceContextConfigurationBuilder.build();
    	}

    	@Bean
    	@ConditionalOnMissingBean
    	public StalactitePlatformTransactionManager platformTransactionManager(PersistenceContextConfiguration persistenceContextConfiguration) {
    		return (StalactitePlatformTransactionManager) persistenceContextConfiguration.getConnectionConfiguration().getConnectionProvider();
    	}

    	@Bean
    	@ConditionalOnMissingBean
    	public Dialect dialect(PersistenceContextConfiguration persistenceContextConfiguration, ObjectProvider<DialectCustomizer> dialectCustomizer) {
    		Dialect dialect = persistenceContextConfiguration.getDialect();
    		nullable(dialectCustomizer.getIfAvailable()).invoke(customizer -> customizer.customize(dialect));
    		return dialect;
    	}

    	@Bean
    	@ConditionalOnMissingBean
    	public ConnectionSettings connectionSettings() {
    		return new ConnectionSettings();
    	}

    	@Bean
    	@ConditionalOnMissingBean
    	public DatabaseVendorSettings databaseVendorSettings(DialectResolver dialectResolver, DataSource dataSource) throws SQLException {
    		DatabaseVendorSettings databaseVendorSettings;
    		try (Connection connection = dataSource.getConnection()) {
    			databaseVendorSettings = dialectResolver.determineVendorSettings(connection);
    		}
    		return databaseVendorSettings;
    	}

    	@Bean
    	@ConditionalOnMissingBean
    	public DialectResolver dialectResolver() {
    		return new ServiceLoaderDialectResolver();
    	}

    	@Bean
    	@ConditionalOnMissingBean
    	public StalactitePlatformTransactionManager stalactitePlatformTransactionManager(DataSource dataSource) {
    		return new StalactitePlatformTransactionManager(dataSource);
    	}

    	static class StalactiteRepositoriesImportSelector implements ImportSelector {

    		@Override
    		public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    			return new String[] { StalactiteRepositoriesRegistrar.class.getName() };
    		}
    	}
    }